package com.chenju.invokechainservice.utils;

import com.chenju.invokechainservice.common.enums.ChangeType;
import com.chenju.invokechainservice.common.exceptions.FunnyValidationException;
import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.api.errors.GitAPIException;
import org.eclipse.jgit.diff.DiffEntry;
import org.eclipse.jgit.errors.IncorrectObjectTypeException;
import org.eclipse.jgit.errors.MissingObjectException;
import org.eclipse.jgit.lib.AnyObjectId;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.ObjectReader;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevTree;
import org.eclipse.jgit.revwalk.RevWalk;
import org.eclipse.jgit.treewalk.AbstractTreeIterator;
import org.eclipse.jgit.treewalk.CanonicalTreeParser;
import org.eclipse.jgit.treewalk.TreeWalk;
import org.eclipse.jgit.treewalk.filter.*;

import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class GitRepositoryComparator {

    public static Map<String, ChangeType> generateDiffFileMap(String oldRepositoryPath, String newRepositoryPath) {
        if (oldRepositoryPath == null || "".equals(oldRepositoryPath)) {
            throw new FunnyValidationException("原分支代码仓库地址为空");
        }
        if (newRepositoryPath == null || "".equals(newRepositoryPath)) {
            throw new FunnyValidationException("新分支代码仓库地址为空");
        }

        Map<String, ChangeType> fileChangeTypeMap = new HashMap<>();

        try(
                Repository oldRepository = openRepository(oldRepositoryPath);
                Repository newRepository = openRepository(newRepositoryPath)
        ) {
            List<DiffEntry> diffs = compareRepositories(oldRepository, newRepository);
            if (diffs == null) {
                System.out.println("The repositories are identical.");
                return null;
            } else {
                System.out.println("The repositories have differences:");
                for (DiffEntry diff : diffs) {
                    if (diff.getChangeType() == DiffEntry.ChangeType.ADD) {
                        fileChangeTypeMap.put(diff.getNewPath(), ChangeType.ADD);
                    } else if (diff.getChangeType() == DiffEntry.ChangeType.DELETE) {
                        fileChangeTypeMap.put(diff.getOldPath(), ChangeType.DELETE);
                    } else if (diff.getChangeType() == DiffEntry.ChangeType.MODIFY) {
                        fileChangeTypeMap.put(diff.getNewPath(), ChangeType.MODIFY);
                    } else if (diff.getChangeType() == DiffEntry.ChangeType.COPY) {
                        System.out.println("ChangeType.COPY Not Success");
                    } else if (diff.getChangeType() == DiffEntry.ChangeType.RENAME) {
                        System.out.println("ChangeType.RENAME Not Sucess");
                    } else {
                        System.out.println(diff.getChangeType() + " Not Success");
                    }
                }
            }
        }
        return fileChangeTypeMap;
    }

    public static Repository openRepository(String repositoryPath) {
        try {
            Git git = Git.open(new File(repositoryPath));
            return git.getRepository();
        } catch (IOException e) {
            e.printStackTrace();
            return null;
        }
    }

    public static List<DiffEntry> compareRepositories(Repository repo1, Repository repo2){
        try (Git git1 = new Git(repo1);
             Git git2 = new Git(repo2)) {
            AbstractTreeIterator newTree = prepareTreeParser(repo1, repo1.resolve("master"));
            AbstractTreeIterator oldTree = prepareTreeParser(repo2, repo2.resolve("20230801"));
//            TreeFilter treeFilter = PathSuffixFilter.create(".java");
//            TreeFilter treeFilter = PathFilter.create("common/src/main/");
            TreeFilter treeFilter = new TreeFilter() {
                @Override
                public boolean include(TreeWalk treeWalk) throws MissingObjectException, IncorrectObjectTypeException, IOException {
                    return !(treeWalk.getPathString().startsWith(".") || treeWalk.getNameString().endsWith(".gitignore")
                            || treeWalk.getNameString().endsWith(".mvn") || treeWalk.getNameString().endsWith(".cmd")
                            || treeWalk.getNameString().endsWith("mvnw") || treeWalk.getNameString().endsWith(".xmind")
                            || treeWalk.getNameString().endsWith(".md")
                    );
                }

                @Override
                public boolean shouldBeRecursive() {
                    return true;
                }

                @Override
                public TreeFilter clone() {
                    return this;
                }
            };

            return git2.diff()
                    .setOldTree(oldTree)
                    .setNewTree(newTree)
                    .setPathFilter(treeFilter)
                    .call();
        } catch (GitAPIException | IOException e) {
            e.printStackTrace();
        }
        return null;
    }

    public static AbstractTreeIterator prepareTreeParser(Repository repository, AnyObjectId objectId) throws IOException {
        try {
            RevWalk walk = new RevWalk(repository);
            RevTree tree;
            tree = walk.parseTree(objectId);
            CanonicalTreeParser TreeParser = new CanonicalTreeParser();
            try (ObjectReader reader = repository.newObjectReader()) {
                TreeParser.reset(reader, tree.getId());
            }
            walk.dispose();
            return TreeParser;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    public static boolean compareByteArrays(byte[] byteArray1, byte[] byteArray2) {
        if (byteArray1.length != byteArray2.length) {
            return false;
        }

        for (int i = 0; i < byteArray1.length; i++) {
            if (byteArray1[i] != byteArray2[i]) {
                return false;
            }
        }

        return true;
    }

    public static void main(String[] args) {

        String repositoryPath1 = "D:\\Project\\testgit\\master\\.git";
        String repositoryPath2 = "D:\\Project\\testgit\\feature\\.git";
        generateDiffFileMap(repositoryPath1, repositoryPath2);
    }
}
